/*
 * Copyright (c) 2018-2023 北京华恒盛世科技有限公司
 * QUICKPOOL is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/

#include "subcmd.h"
#include "subcmd.history.h"

static struct QuickPoolJobInfo *initialize_jobinfo(void);
static bool_t check_queue(struct recordRequest *, char *);
static bool_t check_host(struct recordRequest *, struct recordJob *);

static struct QuickPoolJobInfo *cmd_copy_jobinfo(struct QuickPoolJobInfo *);
static struct recordJob *create_job_record(int, struct recordRequest *);
static int add_job_record(struct recordJob *);
static int add_record(struct recordContent *, struct recordJob *);

void
free_jobrecord(struct recordJob *record)
{
    gf_list_remove(glistJobRecord, (LIST_T *)record);

    char key[32+1];
    snprintf(key, 32, "%lld", record->job->jobid);
    gf_hash_remove(ghashJobRecord, key);

    gf_xdr_free(libxdr_jobinfo, record->job);
    FREEUP(record->job);

    struct recordContent *currentContent, *nextContent;
    nextContent = record->record_head;
    while (nextContent) {

        currentContent = nextContent;

        switch (currentContent->kind) {
        case RECORD_TYPE_JOB_START:
        case RECORD_TYPE_PRE_EXEC_START:
            if (0 < currentContent->num_execute_node) {

                free_job_usednuma(currentContent->num_execute_node, currentContent->job_numa);
                free_job_usedgpu(currentContent->num_execute_node, currentContent->job_GPU);

                unsigned int i;
                for (i=0; i<currentContent->num_execute_node; i++) {
                    FREEUP(currentContent->execute_nodes[i]);
                    FREEUP(currentContent->num_slot);
                }

                FREEUP(currentContent->execute_nodes);
                currentContent->num_execute_node = 0;
            }

            break;
        case RECORD_TYPE_JOB_EXECUTE:
            FREEUP(currentContent->execute_home);
            FREEUP(currentContent->execute_cwd);
            FREEUP(currentContent->execute_user);

            break;
        case RECORD_TYPE_JOB_MIGRATE:
        {
            unsigned int i;
            for (i = 0; i < currentContent->num_spec_node; i++) {
                FREEUP(currentContent->spec_nodes[i]);
            }
            FREEUP(currentContent->spec_nodes);
            FREEUP(currentContent->user_name);

            break;
        }
        case RECORD_TYPE_JOB_SIGNAL:
            FREEUP(currentContent->user_name);
            FREEUP(currentContent->signal_symbol);

            break;
        case RECORD_TYPE_JOB_SIGACT:
            FREEUP(currentContent->signal_symbol);
            break;
        case RECORD_TYPE_JOB_SWITCH:
        case RECORD_TYPE_JOB_MOVE:
        case RECORD_TYPE_JOB_FORCE:
            FREEUP(currentContent->user_name);
            break;
        }

        nextContent = nextContent->next;
        FREEUP(currentContent);
    }

    FREEUP(record);

    return;
} // end function free_jobrecord

int
get_job_idxlist(char *jobname, int **idxs0)
{
    int maxJobArray;
    struct array_index *jobArrayIndex=NULL;
    jobArrayIndex = parse_jobname(jobname, &maxJobArray, MAXARRAYINDEX);
    if (NULL == jobArrayIndex) {
        *idxs0= NULL;
        return 0;
    }

    int *idxs;
    idxs = (int *)calloc(MAX_JOB_IDS, sizeof(int));
    if (NULL == idxs) {
        perror("calloc");
        exit(-1);
    }

    int sizeOfJobIdArray = MAX_JOB_IDS;
    int numIdxs = 0;

    struct array_index *loopIdx;
    for (loopIdx = jobArrayIndex; loopIdx; loopIdx = loopIdx->next_index) {

        int i, j;
        for(j = loopIdx->start; j <= loopIdx->end; j+= loopIdx->step) {

            if (numIdxs >= sizeOfJobIdArray) {

                sizeOfJobIdArray += MAX_JOB_IDS;

                int *temp;
                temp = (int *)realloc(idxs, sizeOfJobIdArray * sizeof(int));
                if (NULL == temp) {
                    fprintf(stderr, "realloc(%ld) failed due to %m.\n",
                            sizeOfJobIdArray * sizeof(int));
                    exit(4);
                }
                idxs = temp;
            }

            for (i = 0; i < numIdxs; i++) {
                if (j == idxs[i]) {
                    break;
                }
            }

            if (i == numIdxs) {
                idxs[numIdxs++] = j;
            }
        }
    }

    *idxs0 = idxs;

    return (numIdxs);
} // end function get_job_idxlist

struct QuickPoolJobInfo *
read_jobcreate(struct record *log)
{
    struct QuickPoolJobInfo *job;
    struct submitRequest *submit;

    job = initialize_jobinfo();
    submit = job->submit;

    unsigned int i, numxf = 0;
    for (i=0; i<log->numKVs; i++) {
        switch (log->KVs[i].key) {
        case RECORD_KEY_JOBID:
            job->jobid = atoll(log->KVs[i].value);
            break;
        case RECORD_KEY_OPTIONS:
            submit->options = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_OPTIONS2:
            submit->options2 = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_PROCESSOR:
            submit->num_job_slot = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_MAX_PROCESSOR:
            submit->max_job_slot = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_SUBMIT_TIME:
            submit->submit_time = atol(log->KVs[i].value);
            break;
        case RECORD_KEY_BEGIN_TIME:
            submit->begin_time = atol(log->KVs[i].value);
            break;
        case RECORD_KEY_TERMINATE_TIME:
            submit->terminate_time = atol(log->KVs[i].value);
            break;
        case RECORD_KEY_USERNAME:
            submit->user = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_LOGIN_SHELL:
            submit->login_shell = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_SIGNAL:
            submit->window_signal = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_LIMIT_CPU:
            submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_LIMIT_FSIZE:
            submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_LIMIT_DATA:
            submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_LIMIT_STACK:
            submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_LIMIT_CORE:
            submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_LIMIT_RSS:
            submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_LIMIT_NOFILE:
            submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_LIMIT_OPEN_MAX:
            submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_LIMIT_SWAP:
            submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_LIMIT_RUN:
            submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_LIMIT_PROCESS:
            submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_UMASK:
            submit->umask = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_QUEUE:
            submit->queue = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_RESREQ:
            submit->resource_requirement = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_FROM_HOST:
            submit->from_node = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_CWD:
            submit->cwd =copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_SUBMIT_INFILE:
            submit->input_file = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_SUBMIT_OUTFILE:
            submit->output_file = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_SUBMIT_ERRFILE:
            submit->error_file = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_NUM_ASKEDHOST:
            submit->num_spec_node = atoi(log->KVs[i].value);
            if (0 < submit->num_spec_node) {
                submit->spec_nodes = (char **)calloc(submit->num_spec_node, sizeof(char *));
                if (NULL == submit->spec_nodes) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                "calloc", submit->num_spec_node*sizeof(char *));
                    quickpool_errno = ERROR_MEMORY;
                    return NULL;
                }
            }
            break;
        case RECORD_KEY_ASKEDHOST:
            if (0 < submit->num_spec_node) {
                char *str;
                unsigned int len = strlen(log->KVs[i].value);

                str = (char *)calloc(len+1, sizeof(char));
                if (NULL == str) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", len+1);
                    quickpool_errno = ERROR_MEMORY;
                    return NULL;
                }

                char *token;
                char *askedhost = log->KVs[i].value;
                unsigned int j = 0;
                while ((token=get_string_token(&askedhost, " ", str, len+1)) != NULL) {
                    len = strlen(askedhost);

                    if (j >= submit->num_spec_node) {
                        log_message(ERR, NOCLASS, "%s: job[%d]'s numAskedHosts[%d] is less than the string[%s].",
                                    __func__, job->jobid, submit->num_spec_node, log->KVs[i].value);
                        break;
                    }

                    submit->spec_nodes[j++] = copy_string(token);
                }
                FREEUP(str);
            }
            break;
        case RECORD_KEY_JOB_NAME:
            submit->job_name = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_COMMAND:
            submit->command = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_DEPENDENCY:
            submit->dependency = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_PREEXEC:
            submit->prejob_command = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_POSTEXEC:
            submit->postjob_command = copy_string(log->KVs[i].value);
            break;            
        case RECORD_KEY_MAIL_USER:
            submit->mail_user = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_PROJECT_NAME:
            submit->project = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_NUM_XFILE:
            submit->num_exec_files = atoi(log->KVs[i].value);
            if (submit->num_exec_files > 0) {
                submit->exec_files = (struct execution_file *)calloc(submit->num_exec_files,
                                                                     sizeof(struct execution_file));
                if (NULL == submit->exec_files) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                "calloc", submit->num_exec_files*sizeof(struct execution_file));
                    quickpool_errno = ERROR_MEMORY;
                    return NULL;
                }
            }
            break;
        case RECORD_KEY_XFILE:
        {
            unsigned int len = strlen(log->KVs[i].value);
            char *value = log->KVs[i].value;
            char *srcf;
            srcf = (char *)calloc(len+1, sizeof(char));
            if (NULL == srcf) {
                log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", len+1);
                quickpool_errno = ERROR_MEMORY;
                return NULL;
            }

            char *token;
            token = get_string_token(&value," \t<>", srcf, len+1);
            if (NULL != token) {
                strcpy(submit->exec_files[numxf].in_file, token);
            }
            FREEUP(srcf);

            char op[3];
            op[0] = op[1] = op[2] ='\0';
            op[0] = *value++;
            if (!isspace((int)*value))
                op[1]=*value++;

            value = strip_spaces(value, FALSE);
            if (NULL != value) {
                strcpy(submit->exec_files[numxf].out_file, value);
            }

            if (0 == strcmp(op,"<")) {
                submit->exec_files[numxf].direction |= XF_OP_EXEC2SUB;
            } else if (0 == strcmp(op,"<<")) {
                submit->exec_files[numxf].direction |= XF_OP_EXEC2SUB;
                submit->exec_files[numxf].direction |= XF_OP_EXEC2SUB_APPEND;
            } else if (0 == (strcmp(op,"<>")) || (0 == strcmp(op,"><"))) {
                submit->exec_files[numxf].direction |= XF_OP_EXEC2SUB;
                submit->exec_files[numxf].direction |= XF_OP_SUB2EXEC;
            } else if (0 == strcmp(op,">")) {
                submit->exec_files[numxf].direction |= XF_OP_SUB2EXEC;
            } else if (0 == strcmp(op,">>")) {
                submit->exec_files[numxf].direction |= XF_OP_SUB2EXEC;
                submit->exec_files[numxf].direction |= XF_OP_SUB2EXEC_APPEND;
            }

            numxf++;
            break;
        }
        case RECORD_KEY_USER_PRIORITY:
            submit->spec_priority = atoi(log->KVs[i].value);
            job->job_priority = submit->spec_priority;
            break;
        case RECORD_KEY_USER_GROUP:
            submit->user_group = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_CHECKPOINT_DIR:
            submit->chkpnt_dir = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_CHECKPOINT_METHOD:
            submit->chkpnt_method = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_CHECKPOINT_PERIOD:
            submit->chkpnt_duration = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_GPU_NUM:
            submit->num_gpu = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_GPU_MODEL:
            submit->gpu_model = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_GPU_EXCLUSIVE:
            submit->gpu_exclusive = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_GPU_MEM:
            submit->gpu_mem = atof(log->KVs[i].value);
            break;
        case RECORD_KEY_GPU_MPS:
            submit->gpu_mps = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_SUBMIT_HOMEDIR:
            submit->home_dir = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_USERID:
            submit->uid = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_SCHEDULE_HOSTTYPE:
            submit->execute_host_type = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_RESTART_PID:
            submit->execute_jobpid = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_JOB_FILE:
            submit->script = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_JOB_GROUP:
            submit->job_group = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_ABS_RUN_LIMIT: /* Do nothing for above keys */
            break;
        }
    }

    if (NULL == submit->job_name || '\0' == submit->job_name[0]) {
        FREEUP(submit->job_name);
        submit->job_name = copy_string(submit->command);
    }

    return (job);
} // end function read_jobcreate

struct recordJob **
read_jobmodify(struct record *log, int *num, struct recordRequest *request)
{
    unsigned int i;
    char *strJobIds=NULL;
    JOBID_T jobid = 0;
    for (i=0; i<log->numKVs; i++) {
        switch (log->KVs[i].key) {
        case RECORD_KEY_MODIFY_JOBIDS:
            strJobIds = log->KVs[i].value;
            break;
        }
        if (NULL != strJobIds) {
            break;
        }
    }

    *num = 0;
    if (NULL == strJobIds) {
        return NULL;
    }

    unsigned int numJobRec=0;
    char *token;
    unsigned int len = strlen(strJobIds);
    char *ptr = (char *)calloc(len+1, sizeof(char));
    if (NULL == ptr) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", len+1);
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }

    char *head = strJobIds;
    while (NULL != (token=get_string_token(&strJobIds, " ", ptr, len+1))) {
        len = strlen(strJobIds);
        numJobRec ++;
    }

    if (numJobRec == 0) {
        FREEUP(ptr);
        return NULL;
    }

    struct recordJob **recordArray;
    recordArray = (struct recordJob **)calloc(numJobRec, sizeof(struct recordJob *));
    if (NULL == recordArray) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", numJobRec*sizeof(struct recordJob *));
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }

    numJobRec = 0;
    strJobIds = head;
    len = strlen(strJobIds);
    while (NULL != (token=get_string_token(&strJobIds, " ", ptr, len+1))) {
        len = strlen(strJobIds);

        jobid = atoll(token);

        recordArray[numJobRec] = (struct recordJob *)gf_hash_find(ghashJobRecord, token);
        if (NULL == recordArray[numJobRec]) {

            if ((request->options & OPT_CHRONICLE) && is_request_jobid(request, jobid)) {

                recordArray[numJobRec] = create_job_record(jobid, request);
                if (NULL == recordArray[numJobRec]) {
                    FREEUP(ptr);
                    return NULL;
                }
            } else {
                FREEUP(ptr);
                return NULL;
            }
        }

        struct recordContent *content;
        content = (struct recordContent *)calloc(1, sizeof(struct recordContent));
        if (NULL == content) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", sizeof(struct recordContent));
            quickpool_errno = ERROR_MEMORY;
            return NULL;
        }

        content->modify_params = initialize_jobinfo();

        struct submitRequest *submit = content->modify_params->submit;

        unsigned int numxf=0;
        for (i=0; i<log->numKVs; i++) {
            switch (log->KVs[i].key) {
            case RECORD_KEY_OPTIONS:
                submit->options = atoi(log->KVs[i].value);
                break;
            case RECORD_KEY_OPTIONS2:
                submit->options2 = atoi(log->KVs[i].value);
                break;
            case RECORD_KEY_PROCESSOR:
                submit->num_job_slot = atoi(log->KVs[i].value);
                break;
            case RECORD_KEY_MAX_PROCESSOR:
                submit->max_job_slot = atoi(log->KVs[i].value);
                break;
            case RECORD_KEY_SUBMIT_TIME:
                submit->submit_time = atol(log->KVs[i].value);
                break;
            case RECORD_KEY_BEGIN_TIME:
                submit->begin_time = atol(log->KVs[i].value);
                break;
            case RECORD_KEY_TERMINATE_TIME:
                submit->terminate_time = atol(log->KVs[i].value);
                break;
            case RECORD_KEY_USERNAME:
                submit->user = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_LOGIN_SHELL:
                submit->login_shell = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_SIGNAL:
                submit->window_signal = atoi(log->KVs[i].value);
                break;
            case RECORD_KEY_LIMIT_CPU:
                submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
                break;
            case RECORD_KEY_LIMIT_FSIZE:
                submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
                break;
            case RECORD_KEY_LIMIT_DATA:
                submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
                break;
            case RECORD_KEY_LIMIT_STACK:
                submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
                break;
            case RECORD_KEY_LIMIT_CORE:
                submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
                break;
            case RECORD_KEY_LIMIT_RSS:
                submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
                break;
            case RECORD_KEY_LIMIT_NOFILE:
                submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
                break;
            case RECORD_KEY_LIMIT_OPEN_MAX:
                submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
                break;
            case RECORD_KEY_LIMIT_SWAP:
                submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
                break;
            case RECORD_KEY_LIMIT_RUN:
                submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
                break;
            case RECORD_KEY_LIMIT_PROCESS:
                submit->job_ulimit[log->KVs[i].key-RECORD_KEY_LIMIT_CPU] = atoi(log->KVs[i].value);
                break;
            case RECORD_KEY_UMASK:
                submit->umask = atoi(log->KVs[i].value);
                break;
            case RECORD_KEY_QUEUE:
                submit->queue = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_RESREQ:
                submit->resource_requirement = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_FROM_HOST:
                submit->from_node = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_CWD:
                submit->cwd = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_SUBMIT_INFILE:
                submit->input_file = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_SUBMIT_OUTFILE:
                submit->output_file = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_SUBMIT_ERRFILE:
                submit->error_file = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_NUM_ASKEDHOST:
                submit->num_spec_node = atoi(log->KVs[i].value);
                if (0 < submit->num_spec_node) {
                    submit->spec_nodes = (char **)calloc(submit->num_spec_node, sizeof(char *));
                    if (NULL == submit->spec_nodes) {
                        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                    "calloc", submit->num_spec_node*sizeof(char *));
                        quickpool_errno = ERROR_MEMORY;
                        return NULL;
                    }
                }
                break;
            case RECORD_KEY_ASKEDHOST:
                if (0 < submit->num_spec_node) {
                    char *str;
                    unsigned int len = strlen(log->KVs[i].value);

                    str = (char *)calloc(len+1, sizeof(char));
                    if (NULL == str) {
                        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", len+1);
                        quickpool_errno = ERROR_MEMORY;
                        return NULL;
                    }

                    char *token;
                    char *askedhost = log->KVs[i].value;
                    unsigned int j = 0;
                    while ((token=get_string_token(&askedhost, " ", str, len+1)) != NULL) {
                        len = strlen(askedhost);

                        if (j >= submit->num_spec_node) {
                            log_message(ERR, NOCLASS, "%s: job[%d]'s numAskedHosts[%d] is less than the string[%s].",
                                        __func__, recordArray[numJobRec]->job->jobid, submit->num_spec_node, log->KVs[i].value);
                            break;
                        }

                        submit->spec_nodes[j++] = copy_string(token);
                    }
                    FREEUP(str);
                } else {
                    char *str;
                    unsigned int len = strlen(log->KVs[i].value);

                    str = (char *)calloc(len+1, sizeof(char));
                    if (NULL == str) {
                        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", len+1);
                        quickpool_errno = ERROR_MEMORY;
                        return NULL;
                    }

                    char *token;
                    char *askedhost = log->KVs[i].value;
                    unsigned int numHosts=0;
                    while ((token=get_string_token(&askedhost, " ", str, len+1)) != NULL) {
                        len = strlen(askedhost);
                        numHosts ++;
                    }

                    if (0 == numHosts) {
                        break;
                    }

                    submit->num_spec_node = numHosts;
                    submit->spec_nodes = (char **)calloc(submit->num_spec_node, sizeof(char *));
                    if (NULL == submit->spec_nodes) {
                        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                    "calloc", submit->num_spec_node*sizeof(char *));
                        quickpool_errno = ERROR_MEMORY;
                        return NULL;
                    }

                    numHosts = 0;
                    askedhost = log->KVs[i].value;
                    len = strlen(askedhost);
                    while ((token=get_string_token(&askedhost, " ", str, len+1)) != NULL) {
                        len = strlen(askedhost);
                        submit->spec_nodes[numHosts++] = copy_string(token);
                    }
                }
                break;
            case RECORD_KEY_JOB_NAME:
                submit->job_name = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_COMMAND:
                submit->command = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_DEPENDENCY:
                submit->dependency = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_PREEXEC:
                submit->prejob_command = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_POSTEXEC:
                submit->postjob_command = copy_string(log->KVs[i].value);
                break;                
            case RECORD_KEY_MAIL_USER:
                submit->mail_user = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_PROJECT_NAME:
                submit->project = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_NUM_XFILE:
                submit->num_exec_files = atoi(log->KVs[i].value);
                if (submit->num_exec_files > 0) {
                    submit->exec_files = (struct execution_file *)calloc(submit->num_exec_files,
                                                                         sizeof(struct execution_file));
                    if (NULL == submit->exec_files) {
                        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                    "calloc", submit->num_exec_files*sizeof(struct execution_file));
                        quickpool_errno = ERROR_MEMORY;
                        return NULL;
                    }
                }
                break;
            case RECORD_KEY_XFILE:
            {
                unsigned int len = strlen(log->KVs[i].value);
                char *value = log->KVs[i].value;
                char *srcf;
                srcf = (char *)calloc(len+1, sizeof(char));
                if (NULL == srcf) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", len+1);
                    quickpool_errno = ERROR_MEMORY;
                    return NULL;
                }

                char *token;
                token = get_string_token(&value," \t<>", srcf, len+1);
                if (NULL != token) {
                    strcpy(submit->exec_files[numxf].in_file, token);
                }
                FREEUP(srcf);

                char op[3];
                op[0] = op[1] = op[2] ='\0';
                op[0] = *value++;
                if (!isspace((int)*value))
                    op[1]=*value++;

                value = strip_spaces(value, FALSE);
                if (NULL != value) {
                    strcpy(submit->exec_files[numxf].out_file, value);
                }

                if (0 == strcmp(op,"<")) {
                    submit->exec_files[numxf].direction |= XF_OP_EXEC2SUB;
                } else if (0 == strcmp(op,"<<")) {
                    submit->exec_files[numxf].direction |= XF_OP_EXEC2SUB;
                    submit->exec_files[numxf].direction |= XF_OP_EXEC2SUB_APPEND;
                } else if (0 == (strcmp(op,"<>")) || (0 == strcmp(op,"><"))) {
                    submit->exec_files[numxf].direction |= XF_OP_EXEC2SUB;
                    submit->exec_files[numxf].direction |= XF_OP_SUB2EXEC;
                } else if (0 == strcmp(op,">")) {
                    submit->exec_files[numxf].direction |= XF_OP_SUB2EXEC;
                } else if (0 == strcmp(op,">>")) {
                    submit->exec_files[numxf].direction |= XF_OP_SUB2EXEC;
                    submit->exec_files[numxf].direction |= XF_OP_SUB2EXEC_APPEND;
                }

                numxf++;
                break;
            }
            case RECORD_KEY_USER_PRIORITY:
                submit->spec_priority = atoi(log->KVs[i].value);
                content->modify_params->job_priority = submit->spec_priority;
                break;
            case RECORD_KEY_USER_GROUP:
                submit->user_group = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_CHECKPOINT_DIR:
                submit->chkpnt_dir = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_CHECKPOINT_METHOD:
                submit->chkpnt_method = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_CHECKPOINT_PERIOD:
                submit->chkpnt_duration = atoi(log->KVs[i].value);
                break;
            case RECORD_KEY_DELETE_OPTIONS:   
                submit->modn_options = atoi(log->KVs[i].value);
                break;
            case RECORD_KEY_DELETE_OPTIONS2:   
                submit->modn_options2 = atoi(log->KVs[i].value);
                break;                
            case RECORD_KEY_USERID:
                submit->uid = atoi(log->KVs[i].value);
                break;                
            case RECORD_KEY_SCHEDULE_HOSTTYPE:
                submit->execute_host_type = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_RESTART_PID:
                submit->execute_jobpid = atoi(log->KVs[i].value);
                break;                
            case RECORD_KEY_SUBMIT_HOMEDIR:
                submit->home_dir = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_JOB_FILE:
                submit->script = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_JOB_GROUP:
                submit->job_group = copy_string(log->KVs[i].value);
                break;
            case RECORD_KEY_ABS_RUN_LIMIT: /* Do nothing for above keys */
                break;
            }
        }

        content->job_status = recordArray[numJobRec]->current_status;
        content->reasons = recordArray[numJobRec]->job->reason_id;
        content->time_stamp = log->log_time;
        content->kind = RECORD_TYPE_JOB_MODIFY;

        if (add_record(content, recordArray[numJobRec]) == -1) {

            FREEUP(content);
            continue;
        }

        numJobRec ++;
    }
    FREEUP(ptr);

    *num = numJobRec;

    return (recordArray);
} // end function read_jobmodify

struct recordJob *
read_jobrun(struct record *log, struct recordRequest *request)
{
    unsigned int i;
    JOBID_T jobid = 0;
    for (i=0; i<log->numKVs; i++) {

        switch (log->KVs[i].key) {
        case RECORD_KEY_JOBID:
            jobid = atoll(log->KVs[i].value);
            break;
        }

        if (jobid > 0) {
            break;
        }
    }

    char key[32+1];
    struct recordJob *record;
    snprintf(key, 32, "%lld", jobid);
    if (NULL == (record=(struct recordJob *)gf_hash_find(ghashJobRecord, key))) {

        if ((request->options & OPT_CHRONICLE) && is_request_jobid(request, jobid)) {

            record = create_job_record(jobid, request);
            if (NULL == record) {
                return NULL;
            }
        } else {
            return NULL;
        }
    }

    struct QuickPoolJobInfo *job = record->job;

    char strJobId[32];
    for (i=0; i<log->numKVs; i++) {
        switch (log->KVs[i].key) {
        case RECORD_KEY_JOBID: /* already read */
            break;
        case RECORD_KEY_NUM_EXECHOST:
            if (0 < job->num_execute_node) {
                unsigned int num;
                for (num = 0; num < job->num_execute_node; num++) {
                    FREEUP(job->execute_nodes[num]);
                }
                FREEUP(job->execute_nodes);
                FREEUP(job->num_slot);
                job->num_execute_node = 0;
            }

            free_job_usednuma(job->num_execute_node, job->job_numa);
            free_job_usedgpu(job->num_execute_node, job->job_GPU);

            job->num_execute_node = atoi(log->KVs[i].value);
            if (0 < job->num_execute_node) {

                job->execute_nodes = (char **)calloc(job->num_execute_node, sizeof(char *));
                if (NULL == job->execute_nodes) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                "calloc", job->num_execute_node*sizeof(char *));
                    quickpool_errno = ERROR_MEMORY;
                    return NULL;
                }

                job->num_slot = (unsigned int *)calloc(job->num_execute_node, sizeof(int));
                if (NULL == job->num_slot) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                "calloc", job->num_execute_node*sizeof(int));
                    quickpool_errno = ERROR_MEMORY;
                    return NULL;
                }
            }

            break;
        case RECORD_KEY_EXECHOST:
            if (0 < job->num_execute_node) {

                unsigned int len = strlen(log->KVs[i].value);

                char *str;
                str = (char *)calloc(len+1, sizeof(char));
                if (NULL == str) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", len+1);
                    quickpool_errno = ERROR_MEMORY;
                    return NULL;
                }

                char *token;
                char *exechost = log->KVs[i].value;
                unsigned int j = 0;
                while ((token=get_string_token(&exechost, " ", str, len+1)) != NULL) {
                    len = strlen(exechost);

                    if (j >= job->num_execute_node) {
                        log_message(ERR, NOCLASS, "%s: job <%s> number_exec_node <%d> is less than the string <%s>.",
                                    __func__, jobid_2string(job->jobid, strJobId),
                                    job->num_execute_node, log->KVs[i].value);
                        break;
                    }

                    char *strSlot;
                    if (NULL == (strSlot = strstr(token, ":"))) {
                        log_message(ERR, NOCLASS, "%s: job <%s> execution node [%s] has syntax error, expect name:slot cores gpus.",
                                    __func__, jobid_2string(job->jobid, strJobId),
                                    log->KVs[i].value);
                        break;
                    }

                    *strSlot = '\0';
                    strSlot ++;

                    job->execute_nodes[j] = copy_string(token);
                    job->num_slot[j] = atoi(strSlot);

                    token = get_string_token(&exechost, " ", str, len+1);
                    if (NULL == token) {
                        log_message(ERR, NOCLASS, "%s: job <%s> execution node [%s] has syntax error, expect name:slot numgpu numcore.",
                                    __func__, jobid_2string(job->jobid, strJobId),
                                    log->KVs[i].value);
                        break;
                    }

                    unsigned int num = atoi(token);
                    if (0 < num) {
                        if (NULL == job->job_numa) {
                            job->job_numa = (struct jobInfoNuma *)calloc(1,
                                                                         sizeof(struct jobInfoNuma));
                            if (NULL == job->job_numa) {
                                log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                            "calloc", sizeof(struct jobInfoNuma));
                                quickpool_errno = ERROR_MEMORY;
                                return NULL;
                            }

                            job->job_numa->alloc_job_numa = (struct jobHostUsedcore *)calloc(job->num_execute_node, sizeof(struct jobHostUsedcore));
                            if (NULL == job->job_numa->alloc_job_numa) {
                                log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                            "calloc", job->num_execute_node*sizeof(struct jobHostUsedcore));
                                quickpool_errno = ERROR_MEMORY;
                                return NULL;
                            }
                        }

                        job->job_numa->alloc_job_numa[j].used_numa = gf_list_make("job used numa core list");
                    }

                    unsigned int k;
                    for (k=0; k<num; k++) {
                        struct usedCoreInfoDown *core;
                        core = (struct usedCoreInfoDown *)calloc(1, sizeof(struct usedCoreInfoDown));
                        if (NULL == core) {
                            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                        "calloc", sizeof(struct usedCoreInfoDown));
                            quickpool_errno = ERROR_MEMORY;
                            return NULL;
                        }

                        len = strlen(exechost);
                        token = get_string_token(&exechost, " ", str, len+1);
                        if (NULL == token) {
                            log_message(ERR, NOCLASS, "%s: job <%s> execution node [%s] has syntax error, expect name:slot cores gpus.",
                                        __func__, jobid_2string(job->jobid, strJobId),
                                        log->KVs[i].value);
                            break;
                        }
                        core->index = atoi(token);

                        len = strlen(exechost);
                        token = get_string_token(&exechost, " ", str, len+1);
                        if (NULL == token) {
                            log_message(ERR, NOCLASS, "%s: job <%s> execution node [%s] has syntax error, expect name:slot cores gpus.",
                                        __func__, jobid_2string(job->jobid, strJobId),
                                        log->KVs[i].value);
                            break;
                        }
                        core->index_sibling = atoi(token);

                        len = strlen(exechost);
                        token = get_string_token(&exechost, " ", str, len+1);
                        if (NULL == token) {
                            log_message(ERR, NOCLASS, "%s: job <%s> execution node [%s] has syntax error, expect name:slot cores gpus.",
                                        __func__, jobid_2string(job->jobid, strJobId),
                                        log->KVs[i].value);
                            break;
                        }
                        core->parent_index = atoi(token);

                        len = strlen(exechost);
                        token = get_string_token(&exechost, " ", str, len+1);
                        if (NULL == token) {
                            log_message(ERR, NOCLASS, "%s: job <%s> execution node [%s] has syntax error, expect name:slot cores gpus.",
                                        __func__, jobid_2string(job->jobid, strJobId),
                                        log->KVs[i].value);
                            break;
                        }
                        core->pparent_index = atoi(token);

                        gf_list_insert(job->job_numa->alloc_job_numa[j].used_numa,
                                       job->job_numa->alloc_job_numa[j].used_numa->forw,
                                       (LIST_T *)core);
                    }

                    len = strlen(exechost);
                    token = get_string_token(&exechost, " ", str, len+1);
                    if (NULL == token) {
                        log_message(ERR, NOCLASS, "%s: job <%s> execution node [%s] has syntax error, expect name:slot cores gpus.",
                                    __func__, jobid_2string(job->jobid, strJobId),
                                    log->KVs[i].value);
                        break;
                    }

                    num = atoi(token);
                    if (0 < num) {
                        if (NULL == job->job_GPU) {
                            job->job_GPU = (struct jobInfoGPU *)calloc(1, sizeof(struct jobInfoGPU));
                            if (NULL == job->job_GPU) {
                                log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                            "calloc", sizeof(struct jobInfoGPU));
                                quickpool_errno = ERROR_MEMORY;
                                return NULL;
                            }

                            job->job_GPU->alloc_job_gpu = (struct jobHostGPUinfo *)calloc(job->num_execute_node, sizeof(struct jobHostGPUinfo));
                            if (NULL == job->job_GPU->alloc_job_gpu) {
                                log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                            "calloc", job->num_execute_node*sizeof(struct jobHostGPUinfo));
                                quickpool_errno = ERROR_MEMORY;
                                return NULL;
                            }
                        }

                        job->job_GPU->alloc_job_gpu[j].num_gpu = num;
                        job->job_GPU->alloc_job_gpu[j].gpus = (struct gpuInfo *)calloc(num, sizeof(struct gpuInfo));
                        if (NULL == job->job_GPU->alloc_job_gpu[j].gpus) {
                            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                        "calloc", num*sizeof(struct gpuInfo));
                            quickpool_errno = ERROR_MEMORY;
                            return NULL;
                        }
                    }

                    for (k=0; k<num; k++) {

                        len = strlen(exechost);
                        token = get_string_token(&exechost, " ", str, len+1);
                        if (NULL == token) {
                            log_message(ERR, NOCLASS, "%s: job <%s> execution node [%s] has syntax error, expect name:slot cores gpus.",
                                        __func__, jobid_2string(job->jobid, strJobId),
                                        log->KVs[i].value);
                            return FALSE;
                        }
                        job->job_GPU->alloc_job_gpu[j].gpus[k].index = atoi(token);
                    }

                    len = strlen(exechost);
                    j ++;
                }
                FREEUP(str);
            }

            break;
        case RECORD_KEY_JOB_PID:
            record->job_pid = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_JOB_PGID:
            record->job_gid = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_USER_GROUP:
            FREEUP(job->submit->user_group);
            job->submit->user_group = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_JOB_STATUS:
        case RECORD_KEY_JOB_FLAG:
        case RECORD_KEY_QUEUE_PRECMD:
        case RECORD_KEY_QUEUE_POSTCMD:
            break;
        }
    }

    job->start_time = log->log_time;

    return (record);
} // end function read_jobrun

struct recordJob *
read_jobstarted(struct record *log, struct recordRequest *request)
{
    unsigned int i;
    JOBID_T jobid = 0;
    for (i=0; i<log->numKVs; i++) {

        switch (log->KVs[i].key) {
        case RECORD_KEY_JOBID:
            jobid = atoll(log->KVs[i].value);
            break;
        }

        if (jobid > 0) {
            break;
        }
    }

    char key[32+1];
    struct recordJob *record;
    snprintf(key, 32, "%lld", jobid);
    if (NULL == (record=(struct recordJob *)gf_hash_find(ghashJobRecord, key))) {

        if ((request->options & OPT_CHRONICLE) && is_request_jobid(request, jobid)) {

            record = create_job_record(jobid, request);
            if (NULL == record) {
                return NULL;
            }
        } else {
            return NULL;
        }
    }

    for (i=0; i<log->numKVs; i++) {
        switch (log->KVs[i].key) {
        case RECORD_KEY_JOBID: /* already read */
            break;
        case RECORD_KEY_JOB_PID:
            record->job_pid = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_JOB_PGID:
            record->job_gid = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_EXEC_HOME:
            FREEUP(record->job->execute_homedir);
            record->job->execute_homedir = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_EXEC_CWD:
            FREEUP(record->job->execute_cwd);
            record->job->execute_cwd = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_EXEC_USERNAME:
            FREEUP(record->job->execute_user);
            record->job->execute_user = copy_string(log->KVs[i].value);
            break;
        }
    }

    return record;
} // end function read_jobstarted

struct recordJob *
read_jobstatus(struct record *log, struct recordRequest *request)
{
    unsigned int i;
    JOBID_T jobid = 0;
    for (i=0; i<log->numKVs; i++) {

        switch (log->KVs[i].key) {
        case RECORD_KEY_JOBID:
            jobid = atoll(log->KVs[i].value);
            break;
        }

        if (jobid > 0) {
            break;
        }
    }

    char key[32+1];
    struct recordJob *record;
    snprintf(key, 32, "%lld", jobid);
    if (NULL == (record=(struct recordJob *)gf_hash_find(ghashJobRecord, key))) {

        if ((request->options & OPT_CHRONICLE) && is_request_jobid(request, jobid)) {

            record = create_job_record(jobid, request);
            if (NULL == record) {
                return NULL;
            }
        } else {
            return NULL;
        }
    }

    struct recordContent *content;
    content = (struct recordContent *)calloc(1, sizeof(struct recordContent));
    if (NULL == content) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct recordContent));
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }

    for (i=0; i<log->numKVs; i++) {
        switch (log->KVs[i].key) {
        case RECORD_KEY_JOB_CPUTIME:
            record->job->used_cpu_time  = atof(log->KVs[i].value);
            content->cpu_time = record->job->used_cpu_time;
            break;
        case RECORD_KEY_JOB_STATUS:
            content->job_status = atoi(log->KVs[i].value);
            record->job->status = content->job_status;
            break;
        case RECORD_KEY_JOB_ENDTIME:
            record->job->end_time = atol(log->KVs[i].value);
            break;
        case RECORD_KEY_NEW_REASON:
            content->reasons = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_SUSP_REASON:
            content->subreasons = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_JOB_EXITCODE:
            record->job->exit_stat = atoi(log->KVs[i].value);
            content->exit_code = record->job->exit_stat;
            break;
        }
    }

    content->time_stamp = log->log_time;
    content->kind = RECORD_TYPE_JOB_STATUS;

    if (add_record(content, record) == -1) {
        FREEUP(content);
        return NULL;
    }

    return record;
} // end function read_jobstatus

struct recordJob *
read_jobsigact(struct record *log, struct recordRequest *request)
{
    unsigned int i;
    JOBID_T jobid = 0;
    for (i=0; i<log->numKVs; i++) {

        switch (log->KVs[i].key) {
        case RECORD_KEY_JOBID:
            jobid = atoll(log->KVs[i].value);
            break;
        }

        if (jobid > 0) {
            break;
        }
    }

    char key[32+1];
    struct recordJob *record;
    snprintf(key, 32, "%lld", jobid);
    if (NULL == (record=(struct recordJob *)gf_hash_find(ghashJobRecord, key))) {

        if ((request->options & OPT_CHRONICLE) && is_request_jobid(request, jobid)) {

            record = create_job_record(jobid, request);
            if (NULL == record) {
                return NULL;
            }
        } else {
            return NULL;
        }
    }

    struct recordContent *content;
    content = (struct recordContent *)calloc(1, sizeof(struct recordContent));
    if (NULL == content) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct recordContent));
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }

    for (i=0; i<log->numKVs; i++) {
        switch (log->KVs[i].key) {
        case RECORD_KEY_JOB_ACTPID:
            content->action_pid = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_JOB_ACTSTATUS:
            content->action_status = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_JOB_ACTFLAG:
            content->act_flags = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_JOB_STATUS:
            content->job_status = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_CHECKPOINT_PERIOD:
            content->chkpnt_period = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_JOB_REASON:
            content->reasons = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_SIGNAL_SYMBOL:
            content->signal_symbol = copy_string(log->KVs[i].value);
            break;
        }
    }

    content->kind = RECORD_TYPE_JOB_SIGACT;
    content->time_stamp = log->log_time;

    if (add_record(content, record) == -1) {
        FREEUP(content);
        return NULL;
    }

    return record;
} // end function read_jobsigact

struct recordJob *
read_jobrequeue(struct record *log, struct recordRequest *request)
{
    unsigned int i;
    JOBID_T jobid = 0;
    for (i=0; i<log->numKVs; i++) {

        switch (log->KVs[i].key) {
        case RECORD_KEY_JOBID:
            jobid = atoll(log->KVs[i].value);
            break;
        }

        if (jobid > 0) {
            break;
        }
    }

    char key[32+1];
    struct recordJob *record;
    snprintf(key, 32, "%lld", jobid);
    if (NULL == (record=(struct recordJob *)gf_hash_find(ghashJobRecord, key))) {

        if ((request->options & OPT_CHRONICLE) && is_request_jobid(request, jobid)) {

            record = create_job_record(jobid, request);
            if (NULL == record) {
                return NULL;
            }
        } else {
            return NULL;
        }
    }

    struct recordContent *content;
    content = (struct recordContent *)calloc(1, sizeof(struct recordContent));
    if (NULL == content) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct recordContent));
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }

    content->kind = RECORD_TYPE_JOB_REQUEUE;
    content->time_stamp = log->log_time;

    if (add_record(content, record) == -1) {
        FREEUP(content);
        return NULL;
    }

    return record;
} // end function read_jobrequeue

struct recordJob *
read_jobmigrate(struct record *log, struct recordRequest *request)
{
    unsigned int i;
    JOBID_T jobid = 0;
    for (i=0; i<log->numKVs; i++) {

        switch (log->KVs[i].key) {
        case RECORD_KEY_JOBID:
            jobid = atoll(log->KVs[i].value);
            break;
        }

        if (jobid > 0) {
            break;
        }
    }

    char key[32+1];
    struct recordJob *record;
    snprintf(key, 32, "%lld", jobid);
    if (NULL == (record=(struct recordJob *)gf_hash_find(ghashJobRecord, key))) {

        if ((request->options & OPT_CHRONICLE) && is_request_jobid(request, jobid)) {

            record = create_job_record(jobid, request);
            if (NULL == record) {
                return NULL;
            }
        } else {
            return NULL;
        }
    }

    struct recordContent *content;
    content = (struct recordContent *)calloc(1, sizeof(struct recordContent));
    if (NULL == content) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct recordContent));
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }

    for (i=0; i<log->numKVs; i++) {
        switch (log->KVs[i].key) {
        case RECORD_KEY_USERID:
            content->user_id = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_USERNAME:
            content->user_name = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_NUM_ASKEDHOST:
            content->num_spec_node = atoi(log->KVs[i].value);
            if (0 < content->num_spec_node) {
                content->spec_nodes = (char **)calloc(content->num_spec_node, sizeof(char *));
                if (NULL == content->spec_nodes) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                "calloc", content->num_spec_node*sizeof(char *));
                    quickpool_errno = ERROR_MEMORY;
                    return NULL;
                }
            }
            break;
        case RECORD_KEY_ASKEDHOST:
            if (0 < content->num_spec_node) {

                unsigned int len = strlen(log->KVs[i].value);

                char *str;
                str = (char *)calloc(len+1, sizeof(char));
                if (NULL == str) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", len+1);
                    quickpool_errno = ERROR_MEMORY;
                    return NULL;
                }

                char *token;
                char *askedhost = log->KVs[i].value;
                unsigned int j = 0;
                while ((token=get_string_token(&askedhost, " ", str, len+1)) != NULL) {
                    len = strlen(askedhost);

                    if (j >= content->num_spec_node) {
                        log_message(ERR, NOCLASS, "%s: job[%d]'s numAskedHosts[%d] is less than the string[%s].",
                                    __func__, jobid, content->num_spec_node, log->KVs[i].value);
                        break;
                    }

                    content->spec_nodes[j++] = copy_string(token);
                }
                FREEUP(str);
            }
        }
    }

    content->kind = RECORD_TYPE_JOB_MIGRATE;
    content->job_status = record->current_status;
    content->reasons = record->job->reason_id;
    content->time_stamp = log->log_time;

    if (add_record(content, record) == -1) {
        FREEUP(content);
        return NULL;
    }

    return record;
} // end function read_jobmigrate

struct recordJob *
read_jobsignal(struct record *log, struct recordRequest *request)
{
    unsigned int i;
    JOBID_T jobid = 0;
    for (i=0; i<log->numKVs; i++) {

        switch (log->KVs[i].key) {
        case RECORD_KEY_JOBID:
            jobid = atoll(log->KVs[i].value);
            break;
        }

        if (jobid > 0) {
            break;
        }
    }

    char key[32+1];
    struct recordJob *record;
    snprintf(key, 32, "%lld", jobid);
    if (NULL == (record=(struct recordJob *)gf_hash_find(ghashJobRecord, key))) {

        if ((request->options & OPT_CHRONICLE) && is_request_jobid(request, jobid)) {

            record = create_job_record(jobid, request);
            if (NULL == record) {
                return NULL;
            }
        } else {
            return NULL;
        }
    }

    struct recordContent *content;
    content = (struct recordContent *)calloc(1, sizeof(struct recordContent));
    if (NULL == content) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct recordContent));
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }

    for (i=0; i<log->numKVs; i++) {
        switch (log->KVs[i].key) {
        case RECORD_KEY_USERID:
            content->user_id = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_USERNAME:
            content->user_name = copy_string(log->KVs[i].value);
            break;
        case RECORD_KEY_SIGNAL_SYMBOL:
            content->signal_symbol = copy_string(log->KVs[i].value);
            break;
        }
    }

    content->kind = RECORD_TYPE_JOB_SIGNAL;
    content->job_status = record->current_status;
    content->reasons = record->job->reason_id;
    content->time_stamp = log->log_time;

    if (add_record(content, record) == -1) {
        FREEUP(content);
        return NULL;
    }

    return record;
} // end function read_jobsignal

struct recordJob *
read_jobstarting(struct record *log, struct recordRequest *request)
{
    unsigned int i;
    JOBID_T jobid = 0;
    for (i=0; i<log->numKVs; i++) {

        switch (log->KVs[i].key) {
        case RECORD_KEY_JOBID:
            jobid = atoll(log->KVs[i].value);
            break;
        }

        if (jobid > 0) {
            break;
        }
    }

    char key[32+1];
    struct recordJob *record;
    snprintf(key, 32, "%lld", jobid);
    if (NULL == (record=(struct recordJob *)gf_hash_find(ghashJobRecord, key))) {

        if ((request->options & OPT_CHRONICLE) && is_request_jobid(request, jobid)) {

            record = create_job_record(jobid, request);
            if (NULL == record) {
                return NULL;
            }
        } else {
            return NULL;
        }
    }

    struct recordContent *content;
    content = (struct recordContent *)calloc(1, sizeof(struct recordContent));
    if (NULL == content) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct recordContent));
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }

    content->kind = RECORD_TYPE_JOB_START_ACCEPT;
    content->time_stamp = log->log_time;
    content->job_status = JOB_STATUS_RUN;

    for (i=0; i<log->numKVs; i++) {
        switch (log->KVs[i].key) {
        case RECORD_KEY_JOB_PID:
            content->job_pid = atoi(log->KVs[i].value);
            break;
        }
    }

    if (add_record(content, record) == -1) {
        FREEUP(content);
        return NULL;
    }

    return record;
} // end function read_jobstarting

struct recordJob *
read_jobswitch(struct record *log, struct recordRequest *request)
{
    JOBID_T jobid=0;
    char *queue=NULL, *username=NULL;
    int userid=0;
    unsigned int i;
    for (i=0; i<log->numKVs; i++) {

        switch (log->KVs[i].key) {
        case RECORD_KEY_JOBID:
            jobid = atoll(log->KVs[i].value);
            break;
        case RECORD_KEY_USERID:
            userid = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_USERNAME:
            username = log->KVs[i].value;
            break;
        case RECORD_KEY_QUEUE:
            queue = log->KVs[i].value;
            break;
        }
    }

    if (0 == jobid || NULL == queue) {
        return NULL;
    }

    char key[32+1];
    struct recordJob *record;
    snprintf(key, 32, "%lld", jobid);
    if (NULL == (record=(struct recordJob *)gf_hash_find(ghashJobRecord, key))) {

        if ((request->options & OPT_CHRONICLE) && is_request_jobid(request, jobid)) {

            record = create_job_record(jobid, request);
            if (NULL == record) {
                return NULL;
            }
        } else {
            return NULL;
        }
    }

    struct recordContent *content;
    content = (struct recordContent *)calloc(1, sizeof(struct recordContent));
    if (NULL == content) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct recordContent));
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }

    strcpy(content->queue, queue);

    content->kind = RECORD_TYPE_JOB_SWITCH;
    content->job_status = record->current_status;
    content->reasons = record->job->reason_id;
    content->user_id = userid;
    content->user_name = copy_string(username);
    content->time_stamp = log->log_time;

    if (add_record(content, record) == -1) {
        FREEUP(content);
        return NULL;
    }

    return record;
} // end function read_jobswitch

struct recordJob *
read_jobmove(struct record *log, struct recordRequest *request)
{
    JOBID_T jobid=0;
    int userid=0, position=0, reqcode=TO_TOP;
    char *username=NULL;
    unsigned int i;
    for (i=0; i<log->numKVs; i++) {

        switch (log->KVs[i].key) {
        case RECORD_KEY_JOBID:
            jobid = atoll(log->KVs[i].value);
            break;
        case RECORD_KEY_USERID:
            userid = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_USERNAME:
            username = log->KVs[i].value;
            break;
        case RECORD_KEY_POSITION:
            position = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_REQCODE:
            reqcode = atoi(log->KVs[i].value);
            break;
        }
    }

    if (0 == jobid) {
        return NULL;
    }

    char key[32+1];
    struct recordJob *record;
    snprintf(key, 32, "%lld", jobid);
    if (NULL == (record=(struct recordJob *)gf_hash_find(ghashJobRecord, key))) {

        if ((request->options & OPT_CHRONICLE) && is_request_jobid(request, jobid)) {

            record = create_job_record(jobid, request);
            if (NULL == record) {
                return NULL;
            }
        } else {
            return NULL;
        }
    }

    struct recordContent *content;
    content = (struct recordContent *)calloc(1, sizeof(struct recordContent));
    if (NULL == content) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct recordContent));
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }

    content->kind = RECORD_TYPE_JOB_MOVE;
    content->base = reqcode;
    content->position = position;
    content->user_id = userid;
    content->user_name = copy_string(username);
    content->time_stamp = log->log_time;

    if (add_record(content, record) == -1) {
        FREEUP(content);
        return NULL;
    }

    return record;
} // end function read_jobmove

struct recordJob *
read_jobclean(struct record *log, struct recordRequest *request)
{
    unsigned int i;
    JOBID_T jobid = 0;
    for (i=0; i<log->numKVs; i++) {

        switch (log->KVs[i].key) {
        case RECORD_KEY_JOBID:
            jobid = atoll(log->KVs[i].value);
            break;
        }

        if (jobid > 0) {
            break;
        }
    }

    char key[32+1];
    struct recordJob *record;
    snprintf(key, 32, "%lld", jobid);
    if (NULL == (record=(struct recordJob *)gf_hash_find(ghashJobRecord, key))) {

        if ((request->options & OPT_CHRONICLE) && is_request_jobid(request, jobid)) {

            record = create_job_record(jobid, request);
            if (NULL == record) {
                return NULL;
            }
        } else {
            return NULL;
        }
    }

    struct recordContent *content;
    content = (struct recordContent *)calloc(1, sizeof(struct recordContent));
    if (NULL == content) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct recordContent));
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }

    content->kind = RECORD_TYPE_JOB_CLEAN;
    content->time_stamp = log->log_time;

    if (add_record(content, record) == -1) {
        FREEUP(content);
        return NULL;
    }

    return record;
} // end function read_jobclean

struct recordJob *
read_jobforcerun(struct record *log, struct recordRequest *request)
{
    unsigned int i;
    JOBID_T jobid = 0;
    for (i=0; i<log->numKVs; i++) {

        switch (log->KVs[i].key) {
        case RECORD_KEY_JOBID:
            jobid = atoll(log->KVs[i].value);
            break;
        }

        if (jobid > 0) {
            break;
        }
    }

    char key[32+1];
    struct recordJob *record;
    snprintf(key, 32, "%lld", jobid);
    if (NULL == (record=(struct recordJob *)gf_hash_find(ghashJobRecord, key))) {

        if ((request->options & OPT_CHRONICLE) && is_request_jobid(request, jobid)) {

            record = create_job_record(jobid, request);
            if (NULL == record) {
                return NULL;
            }
        } else {
            return NULL;
        }
    }

    struct recordContent *content;
    content = (struct recordContent *)calloc(1, sizeof(struct recordContent));
    if (NULL == content) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct recordContent));
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }

    for (i=0; i<log->numKVs; i++) {
        switch (log->KVs[i].key) {
        case RECORD_KEY_USERID:
            content->user_id = atoi(log->KVs[i].value);
            break;
        case RECORD_KEY_USERNAME:
            content->user_name = copy_string(log->KVs[i].value);
            break;
        }
    }

    content->kind = RECORD_TYPE_JOB_FORCE;
    content->time_stamp = log->log_time;

    if (add_record(content, record) == -1) {
        FREEUP(content);
        return NULL;
    }

    return record;

} // end function read_jobforcerun

void
parse_record(struct record *log, struct recordRequest *request)
{
    struct QuickPoolJobInfo *job;
    struct submitRequest *submit;
    unsigned int i, oldjobnum = 0;
    int found;

    struct recordContent *content;
    struct recordJob *record;
    switch (log->type) {
    case RECORD_TYPE_JOB_NEW:
        job = read_jobcreate(log);
        if (NULL == job) {
            break;
        }

        submit = job->submit;
        if (((request->options & OPT_ALLUSERS) != OPT_ALLUSERS)
            && strcmp(submit->user, request->user_name)) {

            gf_xdr_free(libxdr_jobinfo, job);
            FREEUP(job);
            return;
        }

        if (request->options & OPT_JOBID) {
            found = FALSE;
            for (i=0; i<request->num_job; i++) {
                if (((job->jobid == ARRAY_JOBID(request->job_ids[i])))
                    || (job->jobid == request->job_ids[i])) {
                    found = TRUE;
                    break;
                }
            }

            if (found == FALSE) {
                gf_xdr_free(libxdr_jobinfo, job);
                FREEUP(job);
                return;
            }
        }

        if (request->options & OPT_JOBNAME) {

            int *idxList;
            if (!issame_jobname(request->job_name, job->submit->job_name)) {
                gf_xdr_free(libxdr_jobinfo, job);
                FREEUP(job);
                return;
            }

            oldjobnum = request->num_job;
            request->num_job += get_job_idxlist(request->job_name, &idxList);
            if (request->num_job - oldjobnum > 0) {
                request->job_ids = (JOBID_T *)realloc(request->job_ids,
                                                      request->num_job*sizeof(JOBID_T));
                if (NULL == request->job_ids) {
                    perror("realloc");
                    exit(-1);
                }

                for (i=oldjobnum; i<request->num_job; i++) {
                    request->job_ids[i] = QP_JOBID(ARRAY_JOBID(job->jobid), idxList[i-oldjobnum]);
                }
            }
            FREEUP(idxList);
        }

        if (check_queue(request, submit->queue)) {
            int numEles = 0, *idxList, i=0;

            if (NULL != job->submit->job_name && '\0' != job->submit->job_name[0]) {
                numEles = get_job_idxlist(job->submit->job_name, &idxList);
            } else {
                idxList = NULL;
            }

            if ((request->options & OPT_ARRAY_INFO) || numEles <= 0) {

                if (!idxList) {
                    idxList = (int *)calloc(1, sizeof(int));
                    if (NULL == idxList) {
                        perror("calloc");
                        exit(-1);
                    }
                }
                numEles = 1;
                idxList[0] = 0;
            }

            for (i = 0; i < numEles; i++) {

                job->jobid = QP_JOBID(ARRAY_JOBID(job->jobid), idxList[i]);

                struct recordJob *recordElement;
                recordElement = (struct recordJob *)calloc(1, sizeof(struct recordJob));
                if (NULL == recordElement) {
                    char printBuf[200+1];
                    snprintf(printBuf, 200, "%s: %s() failed, %m.", __func__, "calloc");
                    perror(printBuf);
                    exit(3);
                }

                recordElement->job = job;
                recordElement->current_status = 0;

                if (add_job_record(recordElement) == -1) {
                    free_jobrecord(recordElement);
                    return;
                }

                content = (struct recordContent *)calloc(1, sizeof(struct recordContent));
                if (NULL == content) {
                    char printBuf[200+1];
                    snprintf(printBuf, 200, "%s: %s() failed, %m.", __func__, "calloc");
                    perror(printBuf);
                    exit(-1);
                }

                if (submit->options2 & SUBOPT2_HOLD){
                    content->job_status = JOB_STATUS_PSUSP;
                } else {
                    content->job_status = JOB_STATUS_PEND;
                }
                content->reasons = REASON_NOT_SCHEDULE;

                if (log->log_time > submit->submit_time) {
                    content->time_stamp = submit->submit_time;
                } else {
                    content->time_stamp = log->log_time;
                }

                content->kind = RECORD_TYPE_JOB_NEW;
                if (add_record(content, recordElement) == -1) {
                    FREEUP(content);
                    return;
                }

                if ((i+1) < numEles) {
                    job = cmd_copy_jobinfo(job);
                }
            }

            if (idxList) {
                FREEUP(idxList);
            }
        } else {
            gf_xdr_free(libxdr_jobinfo, job);
            FREEUP(job);
            return;
        }

        break;
    case RECORD_TYPE_JOB_MODIFY:
    {
        struct recordJob **recordArray;
        int numJobs=0;

        recordArray = read_jobmodify(log, &numJobs, request);
        if (NULL == recordArray) {
            break;
        }

        break;
    }
    case RECORD_TYPE_PRE_EXEC_START:
    case RECORD_TYPE_JOB_START:
        record = read_jobrun(log, request);
        if (NULL == record) {
            break;
        }

        if (check_host(request, record)) {

            content = (struct recordContent *)calloc(1, sizeof(struct recordContent));
            if (NULL == content) {
                char printBuf[200+1];
                snprintf(printBuf, 200, "%s: %s() failed, %m.", __func__, "calloc");
                perror(printBuf);
                exit(-1);
            }

            content->job_status = JOB_STATUS_RUN;
            content->time_stamp = log->log_time;

            content->kind = log->type;
            content->job_pid = record->job_pid;

            content->num_execute_node = record->job->num_execute_node;
            if (0 < content->num_execute_node) {

                content->execute_nodes = (char **)calloc(content->num_execute_node, sizeof(char *));
                if (NULL == content->execute_nodes) {
                    char printBuf[200+1];
                    snprintf(printBuf, 200, "%s: %s() failed, %m.", __func__, "calloc");
                    perror(printBuf);
                    exit(-1);
                }

                content->num_slot = (unsigned int *)calloc(content->num_execute_node, sizeof(int));
                if (NULL == content->num_slot) {
                    char printBuf[200+1];
                    snprintf(printBuf, 200, "%s: %s() failed, %m.", __func__, "calloc");
                    perror(printBuf);
                    exit(-1);
                }

                if (NULL != record->job->job_numa) {
                    content->job_numa = (struct jobInfoNuma *)calloc(1, sizeof(struct jobInfoNuma));
                    if (NULL == content->job_numa) {
                        char printBuf[200+1];
                        snprintf(printBuf, 200, "%s: %s() failed, %m.", __func__, "calloc");
                        perror(printBuf);
                        exit(-1);
                    }

                    content->job_numa->alloc_job_numa = (struct jobHostUsedcore *)calloc(content->num_execute_node, sizeof(struct jobHostUsedcore));
                    if (NULL == content->job_numa->alloc_job_numa) {
                        char printBuf[200+1];
                        snprintf(printBuf, 200, "%s: %s() failed, %m.", __func__, "calloc");
                        perror(printBuf);
                        exit(-1);
                    }
                }

                if (NULL != record->job->job_GPU) {
                    content->job_GPU = (struct jobInfoGPU *)calloc(1, sizeof(struct jobInfoGPU));
                    if (NULL == content->job_GPU) {
                        char printBuf[200+1];
                        snprintf(printBuf, 200, "%s: %s() failed, %m.", __func__, "calloc");
                        perror(printBuf);
                        exit(-1);
                    }

                    content->job_GPU->alloc_job_gpu = (struct jobHostGPUinfo *)calloc(content->num_execute_node, sizeof(struct jobHostGPUinfo));
                    if (NULL == content->job_GPU->alloc_job_gpu) {
                        char printBuf[200+1];
                        snprintf(printBuf, 200, "%s: %s() failed, %m.", __func__, "calloc");
                        perror(printBuf);
                        exit(-1);
                    }
                }

                for (i = 0; i < content->num_execute_node; i++) {
                    content->execute_nodes[i] = copy_string(record->job->execute_nodes[i]);
                    content->num_slot[i] = record->job->num_slot[i];

                    if (NULL != content->job_numa) {

                        content->job_numa->alloc_job_numa[i].used_numa = gf_list_duplicate(record->job->job_numa->alloc_job_numa[i].used_numa, sizeof(struct usedCoreIndoDown *));
                    }

                    if (NULL != content->job_GPU) {
                        content->job_GPU->alloc_job_gpu[i].num_gpu = record->job->job_GPU->alloc_job_gpu[i].num_gpu;

                        if (0 < content->job_GPU->alloc_job_gpu[i].num_gpu) {
                            content->job_GPU->alloc_job_gpu[i].gpus = (struct gpuInfo *)calloc(content->job_GPU->alloc_job_gpu[i].num_gpu, sizeof(struct gpuInfo));
                            if (NULL == content->job_GPU->alloc_job_gpu[i].gpus) {
                                char printBuf[200+1];
                                snprintf(printBuf, 200, "%s: %s() failed, %m.", __func__, "calloc");
                                perror(printBuf);
                                exit(-1);
                            }

                            unsigned int j;
                            for (j=0; j<content->job_GPU->alloc_job_gpu[i].num_gpu; j++) {
                                content->job_GPU->alloc_job_gpu[i].gpus[j].model = copy_string(record->job->job_GPU->alloc_job_gpu[i].gpus[j].model);
                                content->job_GPU->alloc_job_gpu[i].gpus[j].alloc_mem = record->job->job_GPU->alloc_job_gpu[i].gpus[j].alloc_mem;
                                content->job_GPU->alloc_job_gpu[i].gpus[j].factor = record->job->job_GPU->alloc_job_gpu[i].gpus[j].factor;
                                content->job_GPU->alloc_job_gpu[i].gpus[j].index = record->job->job_GPU->alloc_job_gpu[i].gpus[j].index;
                            }
                        }
                    }
                }
            }

            if (add_record(content, record) == -1) {
                if (0 < content->num_execute_node) {
                    for (i=0; i<content->num_execute_node; i++) {
                        FREEUP(content->execute_nodes[i]);
                    }
                    FREEUP(content->execute_nodes);
                    FREEUP(content->num_slot);
                }

                free_job_usednuma(content->num_execute_node, content->job_numa);
                free_job_usedgpu(content->num_execute_node, content->job_GPU);
                content->num_execute_node = 0;

                FREEUP(content);
                return;
            }
        } else {
            free_jobrecord(record);
        }

        break;
    case RECORD_TYPE_JOB_EXECUTE:

        record = read_jobstarted(log, request);
        if (NULL == record) {
            break;
        }

        if (check_host(request, record)) {

            content = (struct recordContent *)calloc(1, sizeof(struct recordContent));
            if (NULL == content) {
                char printBuf[200+1];
                snprintf(printBuf, 200, "%s: %s() failed, %m.", __func__, "calloc");
                perror(printBuf);
                exit(3);
            }

            content->job_status = JOB_STATUS_RUN;
            content->time_stamp = log->log_time;

            content->kind = log->type;
            content->job_pid = record->job_pid;

            content->execute_home = copy_string(record->job->execute_homedir);
            content->execute_cwd = copy_string(record->job->execute_cwd);
            content->execute_user = copy_string(record->job->execute_user);

            if (add_record(content, record) == -1) {
                FREEUP(content->execute_home);
                FREEUP(content->execute_cwd);
                FREEUP(content->execute_user);
                FREEUP(content);
                return;
            }
        } else {
            free_jobrecord(record);
        }

        break;
    case RECORD_TYPE_JOB_STATUS:
        read_jobstatus(log, request);
        break;
    case RECORD_TYPE_JOB_FORCE:
        read_jobforcerun(log, request);
        break;
    case RECORD_TYPE_JOB_SWITCH:
        read_jobswitch(log, request);
        break;
    case RECORD_TYPE_JOB_MIGRATE:
        read_jobmigrate(log, request);
        break;
    case RECORD_TYPE_JOB_SIGNAL:
        read_jobsignal(log, request);
        break;
    case RECORD_TYPE_JOB_START_ACCEPT:
        read_jobstarting(log, request);
        break;
    case RECORD_TYPE_JOB_SIGACT:
        read_jobsigact(log, request);
        break;
    case RECORD_TYPE_JOB_REQUEUE:
        read_jobrequeue(log, request);
        break;
    case RECORD_TYPE_JOB_MOVE:
        read_jobmove(log, request);
        break;  
    case RECORD_TYPE_JOB_CLEAN:
        read_jobclean(log, request);
        break;  
    case RECORD_TYPE_QUEUE_CTRL:
        break;
    }

    return;
} // end function parse_record

bool_t
is_request_jobid(struct recordRequest *request, JOBID_T jobId)
{
    unsigned int i;

    for (i=0; i<request->num_job; i++) {

        if (request->options & OPT_JOBNAME) {
            if (ARRAY_INDEX(request->job_ids[i]) > 0) {
                if ((jobId == request->job_ids[i])
                    || ((ARRAY_JOBID(jobId) == ARRAY_JOBID(request->job_ids[i]))
                        && (ARRAY_INDEX(jobId) == 0))) {
                    return TRUE;
                }
            } else {
                if (ARRAY_JOBID(jobId) == request->job_ids[i]) {
                    return TRUE;
                }
            }
        } else if ((jobId == request->job_ids[i])
                   || (ARRAY_JOBID(request->job_ids[i]) == ARRAY_JOBID(jobId)
                       && (ARRAY_INDEX(request->job_ids[i]) == 0 || ARRAY_INDEX(jobId) == 0))) {
            return TRUE;
        }
    }

    if (request->num_job) {
        return FALSE;
    }

    return TRUE;
} // end function is_request_jobid

/* ------------------------------------------------
 *
 *  below are static functions used in this file
 *
 * ------------------------------------------------ */

static struct QuickPoolJobInfo *
initialize_jobinfo(void)
{
    struct QuickPoolJobInfo *job;
    job = (struct QuickPoolJobInfo *)calloc(1, sizeof(struct QuickPoolJobInfo));
    if (NULL == job) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct QuickPoolJobInfo));
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }

    job->status = JOB_STATUS_PEND;
    job->reason_id = REASON_NOT_SCHEDULE;
    job->job_priority = -1;

    job->submit = (struct submitRequest *)calloc(1, sizeof(struct submitRequest));
    if (NULL == job->submit) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct submitRequest));
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }

    job->submit->spec_priority  = -1;

    int i;
    for (i = 0; i < RESOURCE_LIMIT_NUM; i++) {
        job->submit->job_ulimit[i] = -1;
    }

    return (job);
} // end function initialize_jobinfo

static struct QuickPoolJobInfo *
cmd_copy_jobinfo(struct QuickPoolJobInfo *jobInfo)
{
    struct QuickPoolJobInfo *job;
    struct submitRequest *submit;

    job = initialize_jobinfo();

    job->jobid = jobInfo->jobid;
    job->job_priority = jobInfo->job_priority;

    submit = job->submit;
    copy_submit_request(jobInfo->submit, submit, FALSE);

    return (job);
} // end function cmd_copy_jobinfo

static bool_t
check_queue(struct recordRequest *request, char *queue)
{
    if ((!strcmp(queue, request->queue) || ((request->options & OPT_QUEUE) != OPT_QUEUE))) {
        return TRUE;
    }

    return FALSE;
} // end function check_queue

static bool_t
check_host(struct recordRequest *request, struct recordJob *record)
{
    unsigned int i;
    if ((request->options & OPT_HOST) != OPT_HOST) {
        return TRUE;
    }

    for (i=0; i<record->job->num_execute_node; i++) {
        if (!strcmp(request->node, record->job->execute_nodes[i])) {
            return TRUE;
        }
    }

    return FALSE;
} // end function check_host

static struct recordJob *
create_job_record(int jobId, struct recordRequest *request)
{
    struct recordJob *record;
    struct QuickPoolJobInfo *job;

    job = initialize_jobinfo();
    job->jobid = jobId;

    job->submit->user = copy_string(request->user_name);
    job->submit->project = copy_string(request->project_name);
    job->submit->queue = copy_string(request->queue);

    record=(struct recordJob *)calloc(1, sizeof(struct recordJob));
    if (NULL == record) {
        perror("calloc");
        exit(-1);
    }

    record->job = job;
    record->current_status = 0;
    if (add_job_record(record) == -1) {
        free_jobrecord(record);
        return NULL;
    }

    return (record);
} // end function create_job_record

static int
add_job_record(struct recordJob *record)
{
    record->job->start_time = 0;
    record->job->end_time = 0;
    record->current_status = JOB_STATUS_PEND;
    record->record_head = NULL;
    record->record_tail = NULL;
    record->forw = NULL;
    record->back = NULL;

    char key[32+1];
    struct recordJob *hashRecord;
    snprintf(key, 32, "%lld", record->job->jobid);
    if (NULL == (hashRecord = (struct recordJob *)gf_hash_find(ghashJobRecord, key))) {
        gf_hash_install(ghashJobRecord, key, (void *)record);
    } else {

        if (record->job->submit->submit_time == hashRecord->job->submit->submit_time) {
            free_jobrecord(hashRecord);
            gf_hash_install(ghashJobRecord, key, (void *)record);
        } else {
            hashRecord = record;
        }
    }

    gf_list_insert(glistJobRecord, (LIST_T *)glistJobRecord->forw, (LIST_T *)record);

    return 0;
} // end function add_job_record

static int
add_record(struct recordContent *content, struct recordJob *record)
{
    content->next = NULL;
    record->current_status = content->job_status;
    record->job->reason_id = content->reasons;

    if (content->job_status & (JOB_STATUS_DONE | JOB_STATUS_EXIT)) {
        record->job->end_time = content->time_stamp;
    }

    if (NULL == record->record_head) {
        record->record_head = record->record_tail = content;
    } else {
        struct recordContent *tailRecord;

        tailRecord = record->record_tail;
        if (content->kind == RECORD_TYPE_JOB_SWITCH) {
            if ((content->time_stamp < tailRecord->time_stamp)
                || ((content->time_stamp == tailRecord->time_stamp)
                    && (content->kind == tailRecord->kind))) {
                return -1;
            }
        } else if (content->kind == RECORD_TYPE_JOB_MIGRATE
                   || content->kind == RECORD_TYPE_JOB_SIGACT) {
            if (content->time_stamp < tailRecord->time_stamp) {
                return -1;
            }
        } else {
            if ((content->time_stamp < tailRecord->time_stamp)
                || ((content->time_stamp == tailRecord->time_stamp)
                    && (content->kind == tailRecord->kind)
                    && (content->job_status == tailRecord->job_status))) {
                return -1;
            }
        }

        record->record_tail->next = content;
        record->record_tail = content;
    }

    content->job = record;

    return 0;
} // end function add_record
